package cii_lib;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;


public class JSserver {
	
	

  public static enum ACCESS {SUCESS, ERROR};

  private Socket Socket;
  private int Step;
  private PrintWriter Out;
  private BufferedReader In;

  public JSserver(int step){
    Socket = null;
    Step   = step;
  }

  public JSserver(){
    Socket = null;
    Step   = 1;
  }

  public void Open(String ip, int port) throws JSserverException {
    JSserverException.ACTION Action = JSserverException.ACTION.OPEN_SERVER;
    try{
      Socket= new Socket(ip,port);
      Action = JSserverException.ACTION.OPEN_TX_CHAN;
      Out = new PrintWriter(Socket.getOutputStream(), true);
      Action = JSserverException.ACTION.OPEN_RX_CHAN;
      In = new BufferedReader(new InputStreamReader(Socket.getInputStream()));
    } catch (Exception e){throw new JSserverException(JSserverException.FUNCTION.OPEN,Action);}
  }

  public void Close() throws JSserverException {
    try{
      Socket.close();
    } catch (Exception e){throw new JSserverException(JSserverException.FUNCTION.CLOSE,JSserverException.ACTION.CLOSE_SOCKET);}
  }

  public void Write(long address, long data) throws JSserverException {
	    JSserverException.ACTION Action = JSserverException.ACTION.TX_CHAN_SEND;
	    try{
	    	//TODO 30 - from parameter
	    	double adrToSend = 30*(Math.pow(2, 20))+address;
	    	int count =2;		
	    	//36864
	    	int cnstCmd = 36864+count; // fst
	    	byte[] cnstCmdB = testbyte(cnstCmd);
	    	byte[] value = testbyte(new Double(adrToSend).longValue());
	    	byte[] dataB = testbyte(data);
	    	byte[] adrCmd = {cnstCmdB[0], cnstCmdB[1],value[0],value[1],value[2],value[3],dataB[0],dataB[1],dataB[2],dataB[3]};
	    	OutputStream out = Socket.getOutputStream(); 
	        DataOutputStream dos = new DataOutputStream(out);
	     
	    	dos.write(adrCmd, 0, adrCmd.length);
//	    	System.out.println("read: "+ readNetLong());
	    	long r =0;
	    	byte[] s =readBytes();
	    	System.out.println("read: "+ (r=bytesToLong(s)));
	      //Out.println("write "+(address*Step)+" "+data);
	    } catch (Exception e){
	    	System.out.println(e);
	    		e.printStackTrace();
	    	throw new JSserverException(JSserverException.FUNCTION.WRITE,Action);}
	  }
  
  public void WriteI2C(byte[] data) throws JSserverException {
	    JSserverException.ACTION Action = JSserverException.ACTION.TX_CHAN_SEND;
	    try{
	    	//TODO
	    	OutputStream out = Socket.getOutputStream(); 
	        DataOutputStream dos = new DataOutputStream(out);
	    	dos.write(data, 0, data.length);
	    	System.out.println("read: "+ readNetLong());
	    	System.out.println("read: "+ readBytes());
	      //Out.println("write "+(address*Step)+" "+data);
	      Action = JSserverException.ACTION.RX_CHAN_REC;
	      
	    } catch (Exception e){throw new JSserverException(JSserverException.FUNCTION.WRITE,Action);}
	  }
  
  public long Read(long address) throws JSserverException {
	    JSserverException.ACTION Action = JSserverException.ACTION.TX_CHAN_SEND;
	    try{
	    	//TODO 30 - from parameter
	    	double adrToSend = 30*(Math.pow(2, 20))+address;
//	    	int extBufLeng = 262000;
	    	int count =1;
	    	int cnstCmd = 57345; // 1 224
	    	byte[] cnstCmdB = testbyte(cnstCmd);
	    	byte[] value = testbyte(new Double(adrToSend).longValue());
	    	byte[] adrCmd = {cnstCmdB[0], cnstCmdB[1], 1,0,0,0,value[0],value[1],value[2],value[3],(byte) count,0,0,0};
	    	OutputStream out = Socket.getOutputStream(); 
	        DataOutputStream dos = new DataOutputStream(out);
	        
	    	dos.write(adrCmd, 0, adrCmd.length);
//	    	System.out.println("read: "+ readNetLong());
	    	long r =0;
	    	byte[] s =readBytes();
	    	System.out.println("read: "+ (r=bytesToLong(s)));
	      //Out.println("write "+(address*Step)+" "+data);
	    	return r;
	      
	    } catch (Exception e){
	    	System.out.println(e);
	    		e.printStackTrace();
	    	throw new JSserverException(JSserverException.FUNCTION.WRITE,Action);}
	  }
  
  
  public void writeNetLong (long address,long data) throws JSserverException {
	    JSserverException.ACTION Action = JSserverException.ACTION.TX_CHAN_SEND;
	    try{
	    	//TODO 30 - from parameter
	    	double adrToSend = 30*(Math.pow(2, 20))+address;
	    	int extBufLeng = 262000;
	    	int count =1;		
	    	//36864
	    	int cnstCmd = 36864+count; // fst
	    	byte[] cnstCmdB = testbyte(cnstCmd);
	    	byte[] value = testbyte(new Double(adrToSend).longValue());
	    	byte[] dataB = testbyte(data);
	    	byte[] adrCmd = {cnstCmdB[0], cnstCmdB[1],value[0],value[1],value[2],value[3],dataB[0],dataB[1],dataB[2],dataB[3]};
	    	OutputStream out = Socket.getOutputStream(); 
	        DataOutputStream dos = new DataOutputStream(out);
	     
	    	dos.write(adrCmd, 0, adrCmd.length);
//	    	System.out.println("read: "+ readNetLong());
	    	long r =0;
	    	byte[] s =readBytes();
	    	System.out.println("read: "+ (r=bytesToLong(s)));
	      //Out.println("write "+(address*Step)+" "+data);
	    } catch (Exception e){
	    	System.out.println(e);
	    		e.printStackTrace();
	    	throw new JSserverException(JSserverException.FUNCTION.WRITE,Action);}
	  }

  public byte[] readBytes() throws IOException {
	    // Again, probably better to store these objects references in the support class
	    InputStream in = Socket.getInputStream();
	    Socket.setSoTimeout(5000);
	    DataInputStream dis = new DataInputStream(in);
	    System.out.println(dis.available());
	    int len = 4;
	    byte[] data = new byte[len];
	    data[1]=8;
	    if (len > 0) {
	        System.out.println("read: "+dis.read(data));
	    }
//	    ByteBuffer bb = ByteBuffer.allocate(4);
//	    bb.put(data);
//	    System.out.println("data: "+ bb.getLong());
	    return data;
	}
  
  public long readNetLong() throws IOException {
	    // Again, probably better to store these objects references in the support class
	    InputStream in = Socket.getInputStream();
	    Socket.setSoTimeout(5000);
	    DataInputStream dis = new DataInputStream(in);
	    System.out.println(dis.available());
	    long data = -1;
	    data=(long)dis.readDouble();
	        System.out.println("read: "+data);
	    
	    return data;
	}
  
//  public long Read(long address)  throws JSserverException {
//    if (address<0) try {
//      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
//    } catch (JSserverException e) {e.printStackTrace();}
//    JSserverException.ACTION Action = JSserverException.ACTION.TX_CHAN_SEND;
//    try{
//      Out.println("read "+address*Step);
//      Action = JSserverException.ACTION.RX_CHAN_REC;
//      String str= In.readLine();
//      In.readLine();
//      Action = JSserverException.ACTION.DATA_CONV;
//      return(Long.decode(str.substring(5)).longValue());
//    } catch (Exception e){throw new JSserverException(JSserverException.FUNCTION.READ,Action);}
//  }



  
  private int _AreaSize(int num){
    if ((num--)==0) return(0);
    int cnt=1;
    while(num>1){
      num/=2;
      cnt++;
    }
    return(1<<cnt);
  }
  public void CII_AREA_Write(int data_width, int width, int numbers, int groups, long addr, int num_pos,int grp_pos, CIIdata data) throws JSserverException{
    if (data_width<=0 || width<=0 || numbers<=0 || groups<=0 || addr<0 || num_pos<0 || grp_pos<0 || numbers<=num_pos || groups<=grp_pos) try {
      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
    } catch (JSserverException e) {e.printStackTrace(); return;}
    if (width==0 || numbers==0 || groups==0) return;
    int PartNum = (width-1)/data_width+1;
    int LastWidth = width-(width/data_width)*data_width;
    int AreaWidth=_AreaSize(numbers);
    long PartAddr = addr + grp_pos*AreaWidth*PartNum + num_pos;
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0)
        Write(PartAddr+index*AreaWidth,data.GetValue(index*data_width,data_width));
      else
        Write(PartAddr+index*AreaWidth,data.GetValue(index*data_width,LastWidth));
  }

  public CIIdata CII_AREA_Read(int data_width, int width, int numbers, int groups, long addr, int num_pos,int grp_pos, CIIdata data) throws JSserverException{
    if (data_width<=0 || width<=0 || numbers<=0 || groups<=0 || addr<0 || num_pos<0 || grp_pos<0 || numbers<=num_pos || groups<=grp_pos) try {
      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
    } catch (JSserverException e) {e.printStackTrace(); return(null);}
    if (width==0 || numbers==0 || groups==0) return(null);
    CIIdata Res = data==null ? new CIIdata(width) : data;
    int PartNum = (width-1)/data_width+1;
    int LastWidth = width-(width/data_width)*data_width;
    int AreaWidth=_AreaSize(numbers);
    long PartAddr = addr + grp_pos*AreaWidth*PartNum + num_pos;
    long Data;
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0){
	Data=Read(PartAddr+index*AreaWidth);
        Res.SetValue(index*data_width,data_width,Data);
      }
      else{
	Data=Read(PartAddr+index*AreaWidth);
        Res.SetValue(index*data_width,LastWidth,Data);
      }
    return (Res);
  }

  public void CII_WORD_Write(int data_width, int width, int numbers, int groups, long addr, int num_pos,int grp_pos, CIIdata data) throws JSserverException{
    if (data_width<=0 || width<=0 || numbers<=0 || groups<=0 || addr<0 || num_pos<0 || grp_pos<0 || numbers<=num_pos || groups<=grp_pos) try {
      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
    } catch (JSserverException e) {e.printStackTrace(); return;}
    if (width==0 || numbers==0 || groups==0) return;
    int PartNum = (width-1)/data_width+1;
    int LastWidth = width-(width/data_width)*data_width;
    long PartAddr = addr + PartNum*(grp_pos*numbers + num_pos);
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0)
        Write(PartAddr+index,data.GetValue(index*data_width,data_width));
      else
        Write(PartAddr+index,data.GetValue(index*data_width,LastWidth));
  }

  public CIIdata CII_WORD_Read(int data_width, int width, int numbers, int groups, long addr, int num_pos,int grp_pos, CIIdata data) throws JSserverException{
    if (data_width<=0 || width<=0 || numbers<=0 || groups<=0 || addr<0 || num_pos<0 || grp_pos<0 || numbers<=num_pos || groups<=grp_pos) try {
      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
    } catch (JSserverException e) {e.printStackTrace(); return(null);}
    if (width==0 || numbers==0 || groups==0) return(null);
    CIIdata Res = data==null ? new CIIdata(width) : data;
    int PartNum = (width-1)/data_width+1;
    int LastWidth = width-(width/data_width)*data_width;
    long PartAddr = addr + PartNum*(grp_pos*numbers + num_pos);
    long Data;
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0){
	Data=Read(PartAddr+index);
        Res.SetValue(index*data_width,data_width,Data);
      }
      else{
	Data=Read(PartAddr+index);
        Res.SetValue(index*data_width,LastWidth,Data);
      }
    return (Res);
  }

  public void CII_BITS_Write(int data_width, int width, int numbers, int groups, long addr, int num_pos, int grp_pos, int bpos, CIIdata data) throws JSserverException{
    if (data_width<=0 || width<=0 || numbers<=0 || groups<=0 || addr<0 || num_pos<0 || grp_pos<0 || bpos<0 || numbers<=num_pos || groups<=grp_pos || width*numbers*groups>data_width) try {
      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
    } catch (JSserverException e) {e.printStackTrace();return;}
    if (width==0 || numbers==0 || groups==0) return;
    int Pos = (grp_pos*numbers + num_pos)*width+bpos;
    long Res = Read(addr);
    Res = (Res & (~(((1<<width)-1)<<Pos))) | (data.GetValue()<<Pos);
    Write(addr,Res);
  }

  public CIIdata CII_BITS_Read(int data_width, int width, int numbers, int groups, long addr, int num_pos, int grp_pos, int bpos, CIIdata data) throws JSserverException{
    if (data_width<=0 || width<=0 || numbers<=0 || groups<=0 || addr<0 || num_pos<0 || grp_pos<0 || bpos<0 || numbers<=num_pos || groups<=grp_pos || width*numbers*groups>data_width) try {
      throw new JSserverException(JSserverException.FUNCTION.READ,JSserverException.ACTION.PARAMETER);
    } catch (JSserverException e) {e.printStackTrace(); return(null);}
    if (width==0 || numbers==0 || groups==0) return(null);
    CIIdata Res = data==null ? new CIIdata(width) : data;
    int Pos = (grp_pos*numbers + num_pos)*width+bpos;
    long Val = Read(addr);
    Val = (Val>>Pos) & ((1<<width)-1);
    Res.SetValue(0,width,Val);
    return(Res);
  }
  
  public static byte[] testbyte(long x) {
		    ByteBuffer buffer = ByteBuffer.allocate(32);
		    buffer.putLong(x);
		    byte[] d = buffer.array();
//		    byte[] a = new byte[14];
//		    a[0] =   (byte)1;
//		    a[1]= (byte)224;
//		    a[2]= (byte)1;
//		    a[3]= (byte)0;
//		    a[4]= (byte)0;
//		    a[5]= (byte)0;
//		    a[6]= (byte)21;
//		    a[7]= (byte)0;
//		    a[8]= (byte)224;
//		    a[9]= 1;
//		    a[10]=1;
//		    a[11]=0;
//		    a[12]=0;
//		    a[13]=0;
//		    d= Longs.toByteArray(x);
	  		
		   String test = Long.toBinaryString(x);
	  ByteArrayOutputStream baos=new ByteArrayOutputStream(Long.SIZE/2);
      DataOutputStream dos=new DataOutputStream(baos);
      byte[] result = {0};
      try {
      dos.writeLong(x);
      result=baos.toByteArray();
      System.out.println("result:"+result.toString());
      dos.close();
      } catch (Exception e) {
    	  e.printStackTrace();
      }
//      byte[] data= {(byte) (result[7]& 0xFF),(byte) (result[6]& 0xFF),(byte) (result[5]& 0xFF),(byte) (result[4]& 0xFF)};
      byte[] data= new byte[4];
      data[0]= (byte)(result[7] & 0xFF);
      data[1]= (byte)((result[6]>0)?result[6]:(256+result[6]));
      data[2]= (byte)(result[5] & 0xFF);
      data[3]= (byte)(result[4] & 0xFF);
//      ,(byte) (result[6]& 0xFF),(byte) (result[5]& 0xFF),(byte) (result[4]& 0xFF)};
      
      return data;
		}

  public static int hex2decimal(String s) {
      String digits = "0123456789ABCDEF";
      s = s.toUpperCase();
      int val = 0;
      for (int i = 0; i < s.length(); i++) {
          char c = s.charAt(i);
          int d = digits.indexOf(c);
          val = 16*val + d;
      }
      return val;
  }
  public long bytesToLong(byte[] bytes) {

	    long x =  ((long)(bytes[3] & 0xFF) << 24)
	    		 + ((long)(bytes[2] & 0xFF) << 16)
	    		 + ((long)(bytes[1] & 0xFF) << 8) 
	    		 + (long)(bytes[0] & 0xFF) ;
//	    ByteBuffer buffer = ByteBuffer.wrap(bytes);
//	    System.out.println(buffer.getLong());
	    return x;
	}
  
  public double toDouble(byte[] bytes) {
	    return ByteBuffer.wrap(bytes).getDouble();
	}
  
  public byte[] initI2C() {
	  char[] v = new char[3];
	  v[0] = (char)(hex2decimal("4002"));
	  v[1] = 'I';
	  v[2] = (char)(2*200);
	  byte[] d = new byte[6];
	  d[0]= (byte)(v[0]%256);
	  d[2]= (byte)(v[1]%256);
	  d[4]= (byte)(v[2]%256);
	  d[1]= (byte)(int)Math.floor(v[0]/256);
	  d[3]= (byte)(int)Math.floor(v[1]/256);
	  d[5]= (byte)(int)Math.floor(v[2]/256);
	  //ByteBuffer byteBuffer = ByteBuffer.allocate(d.length*2);        
      //IntBuffer intBuffer = byteBuffer.asIntBuffer();
      //intBuffer.put(d);
	  System.out.println(d);
	  return d;
  }
  public static void main(String[] args) {
	JSserver s = new JSserver(1);
	try {
		System.out.println(testbyte(57345));
		System.out.println(Long.toBinaryString(31457301L));
		System.out.println(Long.toBinaryString(272495242L));
		s.Open("192.168.228.1", 54321);
//		s.WriteI2C(s.initI2C());
		System.out.println(Long.toBinaryString(s.Read(21)));
//		s.writeNetLong(31457301);
//		long ad = 
	} catch (JSserverException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	
}
}
